package com.coupang.openapi.sdk;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * Coupang OpenAPI HMAC 签名生成工具类
 * 基于 HMAC-SHA256 算法生成签名
 * 参考文档：https://developers.coupangcorp.com/hc/zh-cn/articles/360033461914-HMAC-Signature创建
 */
public class Hmac {

    private static final String ALGORITHM = "HmacSHA256";
    private static final String DATE_FORMAT = "yyMMdd'T'HHmmss'Z'";  // 根据官方文档：使用2位年份
    private static final String AUTHORIZATION_FORMAT = "CEA algorithm=%s, access-key=%s, signed-date=%s, signature=%s";

    /**
     * 生成 HMAC 签名
     *
     * @param method HTTP 请求方法 (GET, POST, PUT, DELETE 等)
     * @param path 请求路径 (包含查询参数)
     * @param secretKey 密钥
     * @param accessKey 访问密钥
     * @return 生成的 Authorization 头信息
     * @throws Exception 签名生成异常
     */
    public static String generate(String method, String path, String secretKey, String accessKey) throws Exception {
        try {
            // 获取 GMT 时间戳
            String datetime = getTimestamp();
            
            // 构建待签名的消息
            // 格式: datetime + method + path
            String message = datetime + method + path;
            
            // 调试输出：打印完整的签名消息
            System.out.println("\n=== HMAC 签名详细信息 ===");
            System.out.println("Timestamp: " + datetime);
            System.out.println("Message: " + message);
            System.out.println("Message Length: " + message.length());
            System.out.println("Message Bytes: " + bytesToHexDebug(message.getBytes(StandardCharsets.UTF_8)));
            
            // 使用 HMAC-SHA256 算法生成签名
            String signature = generateSignature(message, secretKey);
            
            System.out.println("Signature (Hex): " + signature);
            
            // 返回 Authorization 头格式
            // 格式: "CEA algorithm=HmacSHA256, access-key=ACCESS_KEY, signed-date=DATETIME, signature=SIGNATURE"
            String authorization = String.format(AUTHORIZATION_FORMAT, ALGORITHM, accessKey, datetime, signature);
            System.out.println("Authorization: " + authorization);
            System.out.println("=========================\n");
            
            return authorization;
            
        } catch (Exception e) {
            throw new Exception("生成 HMAC 签名失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 调试用：将字节数组转换为十六进制字符串（仅显示前50个字节）
     */
    private static String bytesToHexDebug(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        int limit = Math.min(bytes.length, 50);
        for (int i = 0; i < limit; i++) {
            result.append(String.format("%02x ", bytes[i]));
        }
        if (bytes.length > 50) {
            result.append("...");
        }
        return result.toString();
    }

    /**
     * 获取 GMT/UTC 时间戳
     * 格式：yyMMddTHHmmssZ (例如：241015T112253Z)
     * 根据官方文档使用2位年份
     *
     * @return GMT/UTC 格式的时间戳字符串
     */
    private static String getTimestamp() {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        // 使用 GMT 时区（官方文档要求）
        sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
        return sdf.format(new Date());
    }

    /**
     * 使用 HMAC-SHA256 算法生成签名
     *
     * @param message 待签名的消息
     * @param secretKey 密钥
     * @return 十六进制编码的签名字符串（根据官方文档要求）
     * @throws NoSuchAlgorithmException 算法不存在异常
     * @throws InvalidKeyException 无效密钥异常
     */
    private static String generateSignature(String message, String secretKey)
            throws NoSuchAlgorithmException, InvalidKeyException {

        // 创建 HMAC-SHA256 实例
        Mac hmacSha256 = Mac.getInstance(ALGORITHM);

        // 使用密钥初始化
        SecretKeySpec secretKeySpec = new SecretKeySpec(
                secretKey.getBytes(StandardCharsets.UTF_8),
                ALGORITHM
        );
        hmacSha256.init(secretKeySpec);

        // 计算签名
        byte[] hash = hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));

        // 使用十六进制编码（官方文档示例使用 hexdigest）
        return bytesToHex(hash);
    }
    
    /**
     * 将字节数组转换为十六进制字符串
     * 
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
}

